package com.unitd.frame.comm.utils;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;

/**
 * @desc Base58编解码工具类
 * Base58采用的字符集合为“123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ”;
 * 从这不难看出,Base58是纯数字与字母组成而且去掉了容易引起视觉混淆的字符(0:数字零,O:大写O,I:大写i,l:小写L)
 * 9个数字+49个字母=58个。由于没有特殊字符所以在采用鼠标双击或移动设备选择时可以自动识别全选
 * ase58本身就是URLSafe。Base64的URFSafe模式虽然已经对URL支持的比较好,但UUID中还是包含“-或_”
 * 目前流行的比特币,采用的就是Base58Check编码,是在Base58基础上又增加了安全效验机制。
 * @filename Base58.java
 * @copyright www.unitd.com
 * @author Hudan
 * @version 1.0
 * @date 2017/4/10
 */
public class Base58 {
	public static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray();
	private static final int[] INDEXES = new int[128];

	public Base58() {
	}

	static {
		for (int i = 0; i < INDEXES.length; i++) {
			INDEXES[i] = -1;
		}
		for (int i = 0; i < ALPHABET.length; i++) {
			INDEXES[ALPHABET[i]] = i;
		}
	}

	public static String encode(byte[] value) {
		if (value.length == 0) {
			return "";
		} else {
			byte[] input = copyOfRange(value, 0, value.length);

			int zeroCount;
			for (zeroCount = 0; zeroCount < input.length && input[zeroCount] == 0; ++zeroCount) {
				;
			}

			byte[] temp = new byte[input.length * 2];
			int j = temp.length;

			byte output;
			for (int startAt = zeroCount; startAt < input.length; temp[j] = (byte) ALPHABET[output]) {
				output = divmod58(input, startAt);
				if (input[startAt] == 0) {
					++startAt;
				}

				--j;
			}

			while (j < temp.length && temp[j] == ALPHABET[0]) {
				++j;
			}

			while (true) {
				--zeroCount;
				if (zeroCount < 0) {
					byte[] var9 = copyOfRange(temp, j, temp.length);

					try {
						return new String(var9, "US-ASCII");
					} catch (UnsupportedEncodingException var8) {
						throw new RuntimeException(var8);
					}
				}

				--j;
				temp[j] = (byte) ALPHABET[0];
			}
		}
	}

	public static String encode(long id) {
		return encode(long2Bytes(id));
	}

	public static long decode(String input) {
		return bytes2Long(decodeInner(input));
	}

	public static byte[] decodeInner(String input) {
		if (input.length() == 0) {
			return new byte[0];
		} else {
			byte[] input58 = new byte[input.length()];

			int zeroCount;
			int j;
			for (zeroCount = 0; zeroCount < input.length(); ++zeroCount) {
				char temp = input.charAt(zeroCount);
				j = -1;
				if (temp >= 0 && temp < 128) {
					j = INDEXES[temp];
				}

				if (j < 0) {
					throw new RuntimeException("Illegal character " + temp + " at " + zeroCount);
				}

				input58[zeroCount] = (byte) j;
			}

			for (zeroCount = 0; zeroCount < input58.length && input58[zeroCount] == 0; ++zeroCount) {
				;
			}

			byte[] var7 = new byte[input.length()];
			j = var7.length;

			byte mod;
			for (int startAt = zeroCount; startAt < input58.length; var7[j] = mod) {
				mod = divmod256(input58, startAt);
				if (input58[startAt] == 0) {
					++startAt;
				}

				--j;
			}

			while (j < var7.length && var7[j] == 0) {
				++j;
			}

			return copyOfRange(var7, j - zeroCount, var7.length);
		}
	}

	public static byte[] doubleDigest(byte[] input, int offset, int length) {
		try {
			MessageDigest e = MessageDigest.getInstance("SHA-256");
			e.update(input, offset, length);
			byte[] first = e.digest();
			return e.digest(first);
		} catch (NoSuchAlgorithmException var5) {
			throw new RuntimeException(var5);
		}
	}

	private static byte divmod58(byte[] number, int startAt) {
		int remainder = 0;

		for (int i = startAt; i < number.length; ++i) {
			int digit256 = number[i] & 255;
			int temp = remainder * 256 + digit256;
			number[i] = (byte) (temp / 58);
			remainder = temp % 58;
		}

		return (byte) remainder;
	}

	private static byte divmod256(byte[] number58, int startAt) {
		int remainder = 0;

		for (int i = startAt; i < number58.length; ++i) {
			int digit58 = number58[i] & 255;
			int temp = remainder * 58 + digit58;
			number58[i] = (byte) (temp / 256);
			remainder = temp % 256;
		}

		return (byte) remainder;
	}

	private static byte[] copyOfRange(byte[] source, int from, int to) {
		byte[] range = new byte[to - from];
		System.arraycopy(source, from, range, 0, range.length);
		return range;
	}

	public static synchronized String compressedUUID() {
		UUID uuid = UUID.randomUUID();
		byte[] byUuid = new byte[16];
		long least = uuid.getLeastSignificantBits();
		long most = uuid.getMostSignificantBits();
		long2bytes(most, byUuid, 0);
		long2bytes(least, byUuid, 8);
		return encode(byUuid);
	}

	public static byte[] long2Bytes(long num) {
		byte[] byteNum = new byte[8];

		for (int ix = 0; ix < 8; ++ix) {
			int offset = 64 - (ix + 1) * 8;
			byteNum[ix] = (byte) ((int) (num >> offset & 255L));
		}

		return byteNum;
	}

	public static long bytes2Long(byte[] byteNum) {
		long num = 0L;

		for (int ix = 0; ix < 8; ++ix) {
			num <<= 8;
			num |= (long) (byteNum[ix] & 255);
		}

		return num;
	}

	public static void long2bytes(long value, byte[] bytes, int offset) {
		int offsetLocal = offset;

		for (int i = 7; i > -1; --i) {
			bytes[offsetLocal++] = (byte) ((int) (value >> 8 * i & 255L));
		}
	}
}
